package com.flyqiu.flow.bpmn.element;

import com.alibaba.fastjson2.JSONObject;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.flow.bpmn.FlyFlowElementConstants;

import lombok.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

@EqualsAndHashCode(callSuper = true)
@Data
public class ProcessModel extends BaseProcElement {

    private StartEvent startEvent;
    protected List<BaseProcElement> elements;
    protected Map<String, BaseProcElement> elementMap;

    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private boolean isLock = true;

    public boolean isLock() {
        return isLock;
    }

    public void unLock() {
        isLock = false;
    }

    @SuppressWarnings("unchecked")
    public <T extends BaseProcElement> T getElement(String elementDefKey) {
        if (!elementMap.containsKey(elementDefKey)) {
            return null;
        }
        return (T) elementMap.get(elementDefKey);
    }


    @Override
    public String getElementType() {
        return FlyFlowElementConstants.ELEMENT_TYPE_PROCESS;
    }


    @Override
    protected void initElement(JSONObject element, ProcessModel processModel) {

    }

    public List<BaseProcElement> getFlowElements() {
        return elements;
    }

    public <T extends BaseProcElement> List<T> getFlowElements(String elementType, Class<T> clazz) {
        return getFlowElements(clazz, procElement -> elementType.equals(procElement.getElementType()));
    }

    public <T extends BaseProcElement> List<T> getFlowElements(Class<T> clazz, Function<IProcElement, Boolean> mapping) {
        List<T> result = new ArrayList<>();
        if (clazz.isInstance(this) && mapping.apply(this)) {
            result.add(clazz.cast(this));
        }
        if (FlyQiuCollectionTools.isEmpty(elements)) {
            return result;
        }
        Boolean apply;
        for (BaseProcElement procElement : elements) {
            if (!clazz.isInstance(procElement)) {
                continue;
            }
            apply = mapping.apply(procElement);
            if (apply == null) {
                continue;
            }
            if (apply) {
                result.add(clazz.cast(procElement));
            }


        }
        return result;
    }


    public ProcNodeModel getInitialFlowElement() {
        return startEvent;
    }

    public BaseProcElement getFlowElement(String defKey) {
        return elementMap.get(defKey);
    }
}
